﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.IO;
using System.Xml;
using System.Diagnostics;

namespace SchemaGen.Xsd
{
    public class SDocument
    {
        public string FilePath { get; set; }
        public Dictionary<XName, SSimpleType> SimpleTypeDeclarations { get; private set; }
        public Dictionary<XName, SComplexType> ComplexTypeDeclarations { get; private set; }
        public SComplexElement RootElement { get; private set; }
        public XNamespace Namespace { get; private set; }

        public static SDocument GenerateFromType(Type type)
        {
            return new SchemaGenerator(type).Document;
        }

        public SDocument(string rootElementName, string targetNamespace)
        {
            RootElement = new SComplexElement(rootElementName);
            Namespace = targetNamespace;
            SimpleTypeDeclarations = new Dictionary<XName, SSimpleType>();
            ComplexTypeDeclarations = new Dictionary<XName, SComplexType>();
        }

        internal XDocument Create()
        {
            var schemaRoot = new XElement(SNames.Elements.Schema,
                new XAttribute("targetNamespace", Namespace),
                new XAttribute(XNamespace.Xmlns + "xs", SNames.XsdSchemaNamespace.NamespaceName),
                new XAttribute("xmlns", Namespace),
                new XAttribute("elementFormDefault", "qualified"),
                new XAttribute("attributeFormDefault", "unqualified"),
                RootElement.Create());
            
            foreach (var declaration in SimpleTypeDeclarations)
            {
                Debug.Assert(declaration.Key == declaration.Value.Name);
                schemaRoot.Add(declaration.Value.Create());
            }

            foreach (var declaration in ComplexTypeDeclarations) schemaRoot.Add(declaration.Value.Create());
            //{
            //    var complexType = declaration.Value.Create();
            //    complexType.Add(new XAttribute("name", declaration.Key));
            //    schemaRoot.Add(complexType);
            //}

            // have to manually resolve namespace prefixes for value="xs:[...]" attributes
            foreach (var attributeElement in schemaRoot.Descendants()
                .Where(e => e is XElement && 
                (e.Attribute("type") != null || e.Attribute("base") != null)))
            {
                var attribute = attributeElement.Attribute("type") ?? attributeElement.Attribute("base");

                if (attribute.Value.StartsWith("{"))
                {
                    var xname = (XName)attribute.Value;
                    attribute.Value = string.Format("{0}:{1}",
                        schemaRoot.GetPrefixOfNamespace(xname.Namespace),
                        xname.LocalName);
                }
            }

            return new XDocument(new XDeclaration("1.0", "UTF-8", null), schemaRoot);
        }

        #region Save

        /// <summary>
        /// If FilePath isn't set, default name is the root element
        /// </summary>
        public void Save()
        {
            Create().Save(FilePath ??
                Path.Combine(Environment.CurrentDirectory, RootElement.Name + ".xsd"));
        }

        public void Save(string fileName)
        {
            Create().Save(fileName);
        }

        public void Save(Stream stream)
        {
            Create().Save(stream);
        }

        public void Save(TextWriter writer)
        {
            Create().Save(writer);
        }

        public void Save(XmlWriter xmlWriter)
        {
            Create().Save(xmlWriter);
        }

        #endregion

    }
}
